﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using FlatRedBall;
using FlatRedBall.Graphics;
using FlatRedBall.Graphics.Animation;
using FlatRedBall.Input;
using FlatRedBall.Math.Geometry;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

using BulletHellBoxes.Utilities;

namespace BulletHellBoxes.GameObjects.Player
{
    public class Defender : GameObject
    {
        #region Constants
        private const int TOTAL_BULLETS = 100;
        private const float START_X = 15.0f;
        private const float START_Y = 0.0f;
        private const int BUFFER_SIZE = 5;
        private const double DELAY = 0.6;
        #endregion

        #region Fields
        private AxisAlignedRectangle _collision;
        private Bullet[] _bulletPool;
        private int _currentBullet;

        private double _lastBeat;
        private float[] _lastBeatBuffer;
        private float _averageBeat;
        private float _multiplier;
        private float _leeway;
        private int _lastIndex;
        private bool _triggerActive;
        private int _inactiveSeconds;
        private int _bulletBurstNum;

        private SoundEffect _weakShotSound;
        private SoundEffect _strongShotSound;

        #endregion

        #region Properties
        public AxisAlignedRectangle Collision
        {
            get { return _collision; }
        }
        #endregion

        #region Constructor
        public Defender(GameController controller, int pNum)
        {
            PATH = @"Content/GameObjects/Defender/" + pNum + "/";
            NUM_FRAMES = 22;
            FRAME_TIME = 0.06f;

            Initialize();
        }
        #endregion

        #region Initialization Methods
        protected override void Initialize()
        {
            base.Initialize();

            this.RelativePosition.X = START_X;
            this.RelativePosition.Y = START_Y;

            _collision = ShapeManager.AddAxisAlignedRectangle();
            _collision.ScaleX = _sprite.ScaleX * .8f;
            _collision.ScaleY = _sprite.ScaleY * .8f;
            _collision.AttachTo(this, false);
            _collision.Visible = false;

            _bulletPool = new Bullet[TOTAL_BULLETS];
            for (int i = 0; i < TOTAL_BULLETS; i++)
            {
                _bulletPool[i] = new Bullet();
                _bulletPool[i].AttachTo(this, false);
            }
            _currentBullet = 0;
            _bulletBurstNum = 1;

            _lastBeat = 0;
            _lastBeatBuffer = new float[BUFFER_SIZE];
            _lastIndex = -1;

            _averageBeat = 0.0f;
            _leeway = 1.0f;
            _inactiveSeconds = 1;

            // load sound effect
            _weakShotSound = FlatRedBallServices.Load<SoundEffect>(@"Content\Sounds\WeakShot");
            _strongShotSound = FlatRedBallServices.Load<SoundEffect>(@"Content\Sounds\StrongShot");
        }

        #endregion

        #region Staple Methods
        public override void Activity()
        {
            throw new NotImplementedException();
        }

        public void Activity(ButtonState bs, Vector2 stickPos, double stickAngle)
        {
            // Puts a minimum offset between trigger pulls
            if (_triggerActive)
            {
                if (bs == ButtonState.Pushed)
                {
                    DetectBeat();
                    _inactiveSeconds = 1;
                    int halfway = _bulletBurstNum / 2;

                    //Plays weakshot sound when bulletBurstNum is 1-4
                    //Plays strongshot sound when it reaches 5
                    if (_bulletBurstNum < 5)
                    {
                        _weakShotSound.Play();
                    }
                    else
                    {
                        _strongShotSound.Play();
                    }

                    for (int i = 0; i < _bulletBurstNum; i++)
                    {
                        float xDir;
                        float yDir;
                        if (_bulletBurstNum % 2 == 1)
                        {
                            // odd number of burst
                            xDir = (float)Math.Cos(stickAngle - ((i - halfway) * Math.PI / 12));
                            yDir = (float)Math.Sin(stickAngle - ((i - halfway) * Math.PI / 12));
                        }
                        else
                        {
                            // even number of burst
                            if (i >= halfway)
                            {
                                xDir = (float)Math.Cos(stickAngle - ((i + 1 - halfway) * Math.PI / 12));
                                yDir = (float)Math.Sin(stickAngle - ((i + 1 - halfway) * Math.PI / 12));
                            }
                            else
                            {
                                xDir = (float)Math.Cos(stickAngle - ((i - halfway) * Math.PI / 12));
                                yDir = (float)Math.Sin(stickAngle - ((i - halfway) * Math.PI / 12));
                            }
                        }
                        _bulletPool[_currentBullet].Shoot(new Vector2(xDir, yDir));
                        _currentBullet++;
                        _currentBullet = _currentBullet % TOTAL_BULLETS;
                    }
                }
                else if ((TimeManager.CurrentTime - _lastBeat) > (_averageBeat + (3 * _leeway)))
                {
                    double diff = (TimeManager.CurrentTime - _lastBeat) - _averageBeat - _leeway;

                    if (diff > _inactiveSeconds)
                    {
                        _inactiveSeconds++;
                        ScaleDown();
                    }
                }
            }
            else
            {
                if (TimeManager.CurrentTime - _lastBeat > DELAY)
                {
                    _triggerActive = true;
                }
            }

            foreach (Bullet b in _bulletPool)
            {
                b.Activity();
            }
        }

        public override void Destroy()
        {
            base.Destroy();
        
            ShapeManager.Remove(_collision);

            foreach (Bullet b in _bulletPool)
            {
                b.Destroy();
            }
        }

        public CollisionType CollideBullet(Circle attacker)
        {
            CollisionType collide = CollisionType.None;

            foreach (Bullet b in _bulletPool)
            {
                if (b.Active
                    && b.Collision.CollideAgainst(attacker))
                {
                    collide = CollisionType.Hit;
                    return collide;
                }
            }

            return collide;
        }

        public CollisionType CollideZone(Circle attacker)
        {
            CollisionType collide = CollisionType.None;

            if (_collision.CollideAgainst(attacker))
            {
                collide = CollisionType.Win;
                return collide;
            }

            return collide;
        }

        public void Reset()
        {
            this.RelativeX = START_X;
            this.RelativeY = START_Y;

            foreach (Bullet b in _bulletPool)
            {
                b.RelativeX = 0;
                b.RelativeY = 0;
                b.RelativeVelocity = Vector3.Zero;
                b.Visible = false;
            }
        }
        #endregion

        #region Private Methods
        private void DetectBeat()
        {
            double deltaBeat = TimeManager.CurrentTime - _lastBeat;
            _lastBeat = TimeManager.CurrentTime;

            if (_lastIndex == -1)
            {
                // Detect the first trigger pull of the game.
                _lastIndex++;
            }
            else if (_lastIndex == 0)
            {
                // Detect the first trigger pull sequence.
                SaveBeat((float)deltaBeat);
                CalculateAverage();
            }
            else if ((deltaBeat < _averageBeat + _leeway) && (deltaBeat > _averageBeat - _leeway))
            {
                ScaleUp();
                SaveBeat((float)deltaBeat);
                CalculateAverage();
            }
            else
            {
                ScaleDown();
                SaveBeat((float)deltaBeat);
                CalculateAverage();
            }

            _triggerActive = false;

        }

        private void SaveBeat(float deltaBeat)
        {
            // Keep track of total beats in _lastIndex.
            // Save the last BUFFER_SIZE in _lastBeatBuffer using the mod operator
            _lastBeatBuffer[_lastIndex % BUFFER_SIZE] = deltaBeat;
            _lastIndex++;
        }

        private void CalculateAverage()
        {
            // Calculate Average for the first BUFFER_SIZE elements
            if (_lastIndex < BUFFER_SIZE)
            {
                float rTotal = 0.0f;
                for (int i = 0; i < _lastIndex; i++)
                {
                    rTotal += _lastBeatBuffer[i];
                }

                _averageBeat = rTotal / _lastIndex;
            }
            // Calculate Averago for the subsequent elements.
            else
            {
                float rTotal = 0.0f;
                for (int i = 0; i < BUFFER_SIZE; i++)
                {
                    rTotal += _lastBeatBuffer[i];
                }

                _averageBeat = rTotal / BUFFER_SIZE;
            }

            _leeway = _averageBeat / 10;
            if (_leeway > .05f)
            {
                _leeway = .05f;
            }
        }

        private void ScaleDown()
        {
            if (_bulletBurstNum > 1)
            {
                _bulletBurstNum = 1;
            }
        }

        private void ScaleUp()
        {
            if (_bulletBurstNum < 5)
            {
                _bulletBurstNum++;
            }
        }
        #endregion
    }
}
